package org.swim.core.manager;

import java.util.List;

import javax.ejb.Stateless;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
import net.sf.json.util.EnumMorpher;
import net.sf.json.util.JSONUtils;

import org.swim.core.entities.Ability;
import org.swim.core.entities.User;
import org.swim.local.manager.AbilityManagerLocal;
import org.swim.remote.entities.IAbility;
import org.swim.remote.entities.IAbility.Status;
import org.swim.remote.entities.IUser;
import org.swim.remote.exceptions.AbilityException;
import org.swim.remote.exceptions.SwimException;
import org.swim.remote.exceptions.TokenException;
import org.swim.remote.manager.AbilityManagerRemote;
import org.swim.utils.SwimLog;

/**
 * @author Fuma
 *
 */
@Stateless
public class AbilityManager extends SwimManager implements AbilityManagerRemote, AbilityManagerLocal {

	private static JsonConfig ABILITY_JSON_CFG;

	static {
		JSONUtils.getMorpherRegistry().registerMorpher(
				new EnumMorpher(IAbility.Status.class));

		ABILITY_JSON_CFG = new JsonConfig();
		ABILITY_JSON_CFG.setRootClass(Ability.class);
	}

	@PersistenceContext(unitName = "swimds")
	private EntityManager em;

	public IAbility post(JSONObject json) {
		Ability ability = (Ability) JSONSerializer.toJava(
				json.getJSONObject("ability"), ABILITY_JSON_CFG);
		
		em.persist(ability);

		return ability;
		
	}
	
	/*
	 * @Delete on the database without any control
	 */
	public IAbility delete(Integer id) {
		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.id = '"
				+ id + "'");
		Ability ability =  (Ability)q.getSingleResult();
		em.remove(ability);
		return ability;
	}
	
	public IAbility put(JSONObject json) {
		Ability ability = (Ability) JSONSerializer.toJava(json, ABILITY_JSON_CFG);
		em.merge(ability);
		
		return ability;
	}

	public IAbility get(Integer id) {
		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.id = '"
				+ id + "'");
		return (Ability)q.getSingleResult();
	}

	@SuppressWarnings("unchecked")
	public List<IAbility> getAll() {
		Query q = em.createQuery("SELECT a FROM Ability a");
		return q.getResultList();
	}

	public IUser addNewAbility(JSONObject object) throws NamingException, SwimException 
	{
		String tokenUser = object.getString("token");

		Integer id = UserLogged.getUserLogged().checkUserLogged(tokenUser);

		Ability ability = (Ability) JSONSerializer.toJava(
				object.getJSONObject("ability"), ABILITY_JSON_CFG);
		
		SwimLog.getSwimLog().debug(AbilityManager.class, ability.getName());
		
		if(ability.getName() == "")
			throw new AbilityException("INVALID ABILITY");
		
		ability.setName(ability.getName().toUpperCase());

		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.name = (?1)");
		
		if(q.setParameter(1, (ability.getName().toUpperCase())).getResultList().size() == 0)
			em.persist(ability);
		else
			ability = (Ability)q.setParameter(1, (ability.getName().toUpperCase())).getSingleResult();
		
		User user = (User) getUml().get(id);
		
		user.setAnAbility(ability);
		
		return user;
	}

	public IAbility setStatusAbility(JSONObject object) throws TokenException, AbilityException {
		String token = object.getString("administratorToken");

		UserLogged.getUserLogged().administratorCheckLogged(token);
		
		Integer id = object.getInt("abilityId");
		
		Ability ability = (Ability) get(id);
		
		Status status = Status.valueOf(object.getString("status").toUpperCase());
		
		ability.setStatus(status);
		
		em.merge(ability);
		
		return ability;
	}


	@SuppressWarnings("unchecked")
	public List<IAbility> findRequestedAbility(JSONObject object) throws TokenException {
		
		String token = object.getString("administratorToken");

		UserLogged.getUserLogged().administratorCheckLogged(token);
		
		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.status = '"
				+ IAbility.Status.REQUESTED.name() + "'");
		
		return q.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public List<IAbility> findAbilities(JSONObject object) {
		String iniziale = object.getString("search-query");

		String s = "SELECT a FROM Ability a WHERE a.status = '"
				+ IAbility.Status.CONFIRMED.name() + "' and (a.name LIKE (?1) or a.name LIKE (?1))";
		
		SwimLog.getSwimLog().debug(AbilityManager.class,
				"[findNoAbilities] " + s);
		
		Query q = em.createQuery(s);
		
		return q.setParameter(1, (iniziale.toUpperCase() + "%")).getResultList();
	}
	
	/* (non-Javadoc)
	 * @see org.swim.core.manager.AbilityManagerLocal#checkAbility(net.sf.json.JSONObject)
	 */
	public Ability checkAbility(JSONObject object) throws AbilityException
	{
		Ability ability = (Ability) JSONSerializer.toJava(
				object.getJSONObject("ability"), ABILITY_JSON_CFG);
		
		if(ability.getName() == "")
			throw new AbilityException("INVALID ABILITY");
		
		ability.setName(ability.getName().toUpperCase());
		
		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.name = (?1)");
		
		if(q.setParameter(1, (ability.getName())).getResultList().isEmpty())
			throw new AbilityException("INVALID ABILITY");
		
		ability = (Ability)q.setParameter(1, (ability.getName())).getSingleResult();
		
		return ability;
		
	}
	
	public Integer getIdAbility(String abilityName) throws AbilityException
	{
		if(abilityName == "")
			throw new AbilityException("INVALID ABILITY");
		
		abilityName = abilityName.toUpperCase();
		
		Query q = em.createQuery("SELECT a.id FROM Ability a WHERE a.name = (?1)");
		
		if(q.setParameter(1, (abilityName)).getResultList().isEmpty())
			throw new AbilityException("INVALID ABILITY");
		
		return (Integer)q.setParameter(1, (abilityName)).getSingleResult();
	}

	public IAbility administratorAddAbility(JSONObject object)
			throws AbilityException, TokenException{
		
		String token = object.getString("token");

		UserLogged.getUserLogged().administratorCheckLogged(token);

		Ability ability = (Ability) JSONSerializer.toJava(
				object.getJSONObject("ability"), ABILITY_JSON_CFG);
		
		if(ability.getName() == "")
			throw new AbilityException("INVALID ABILITY");
		
		ability.setName(ability.getName().toUpperCase());
		ability.setStatus(Status.CONFIRMED);

		Query q = em.createQuery("SELECT a FROM Ability a WHERE a.name = (?1)");
		
		if(q.setParameter(1, (ability.getName().toUpperCase())).getResultList().size() == 0)
		{
			em.persist(ability);
			return ability;
		}
		else
		{
			Ability abilityData = (Ability) q.setParameter(1, (ability.getName().toUpperCase())).getSingleResult();
			abilityData.setStatus(Status.CONFIRMED);
			em.merge(abilityData);
			return abilityData;
		}
		
		
	}


}
